package com.kornero.oms.moscow;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    private static final Pattern PATTERN_LAT = Pattern.compile("<lat>(\\d{2}\\.\\d+?)</lat>");
    private static final Pattern PATTERN_LNG = Pattern.compile("<lng>(\\d{2}\\.\\d+?)</lng>");
    private static final String OVER_QUERY_LIMIT = "OVER_QUERY_LIMIT";

    private static final int TIMEOUT = 1000;

    public static void main(final String[] args) throws IOException, InterruptedException {

        // Check arguments.
        if (args == null || args.length < 2) {
            logger.error("main(): Must be 2 arguments, but was: {}", Arrays.toString(args));
            return;
        }
        final String inputFileName = args[0];
        final String outputFileName = args[1];

        final File inputFile = new File(inputFileName);
        if (!inputFile.exists() || !inputFile.canRead()) {
            logger.error("main(): Can't open input file: {}", inputFileName);
            return;
        }

        final File outputFile = new File(outputFileName);
        final boolean newFile = outputFile.createNewFile();
        if (!outputFile.exists() || !outputFile.canRead() || !outputFile.canWrite()) {
            if (newFile) {
                logger.error("main(): Can't create output file: {}", outputFileName);
            } else {
                logger.error("main(): Can't open output file: {}", outputFileName);
            }
            return;
        }

        // Parse input file.
        final HSSFWorkbook wb = new HSSFWorkbook(new POIFSFileSystem(new FileInputStream(inputFile)));
        final Sheet firstSheet = wb.getSheetAt(0);
        int skipRows = 2;

        final List<Hospital> hospitals = new ArrayList<>(firstSheet.getPhysicalNumberOfRows());
        for (final Row row : firstSheet) {
            if (skipRows-- > 0) {
                continue;
            }
            final Hospital hospital = getHospital(row);
            if (StringUtils.isBlank(hospital.getLat()) || StringUtils.isBlank(hospital.getLng())) {
                logger.error("Can't find hospital: {}", hospital);
            } else {
                hospitals.add(hospital);
            }
        }

        // Create output file.
        final FileOutputStream fos = new FileOutputStream(outputFile, false);
        final PrintStream printStream = new PrintStream(fos);
        printStream.println("var hospitals = [");
        for (final Hospital hospital : hospitals) {
            printStream.println(hospital.toJSON() + ",");
        }
        printStream.println("];");
        printStream.flush();
        printStream.close();
    }

    private static Hospital getHospital(final Row row) throws UnsupportedEncodingException, InterruptedException {
        final String name = row.getCell(2).getStringCellValue();
        final String address = row.getCell(5).getStringCellValue();
        return getHospital(name, address);
    }

    private static Hospital getHospital(final String name, final String address) throws UnsupportedEncodingException, InterruptedException {
        final String urlToRead = "https://maps.googleapis.com/maps/api/geocode/xml?address=" + URLEncoder.encode(address, "UTF-8");
        final String xml;
        try {
            xml = getHTML(urlToRead);
        } catch (final IOException e) {
            logger.error("getHospital(): ", e);
            Thread.sleep(TIMEOUT);
            return getHospital(name, address);
        }

        if (StringUtils.containsIgnoreCase(xml, OVER_QUERY_LIMIT)) {
            logger.warn("getHospital(): Google server is down. Url = '{}'", urlToRead);
            Thread.sleep(TIMEOUT);
            return getHospital(name, address);
        }

        final String lat = getFirstGroupInPattern(PATTERN_LAT, xml);
        final String lng = getFirstGroupInPattern(PATTERN_LNG, xml);

        return new Hospital(name, address, lat, lng);
    }

    private static String getFirstGroupInPattern(final Pattern latPattern, final String xml) {
        final Matcher matcher = latPattern.matcher(xml);
        if (matcher.find()) {
            return matcher.group(1);
        } else {
            return StringUtils.EMPTY;
        }
    }

    private static String getHTML(final String urlToRead) throws IOException {
        final URL url = new URL(urlToRead);
        final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        final StringBuilder result = new StringBuilder();

        String line;
        try (final BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
            while ((line = rd.readLine()) != null) {
                result.append(line);
            }
        }

        return result.toString();
    }
}